#ifndef __TIMERWHEEL_HPP__
#define __TIMERWHEEL_HPP__

#include <sys/timerfd.h>

using TaskFunctor = std::function<void()>;// 任务类型定义
using ReleaseFunctor = std::function<void()>;// 取消定时任务 类型定义

/* 定时任务类——一般用于执行 定时释放 任务 */
class TimerTask
{
private:
    uint64_t _task_id;// 任务id
    uint32_t _timeout;// 超时时间
    TaskFunctor _task;// 定时任务——需要被执行的回调函数
    ReleaseFunctor _release_task;// 在定时轮中取消对该定时任务管理 的回调函数
    bool _cancel;// 是否取消定时任务
public:
    TimerTask(uint64_t task_id, uint32_t timeout, const TaskFunctor& task, const ReleaseFunctor& release):
                                 _task_id(task_id), _timeout(timeout), _task(task), _cancel(false), _release_task(release)
    {}

    /* 获取超时时间——刷新定时任务用 */
    uint32_t TimeOut() const
    {
        return _timeout;
    }

    /* 取消定时任务——不执行该定时任务的回调函数 */
    void Cancel()
    {
        _cancel = true;
    }

    ~TimerTask()
    {
        // DBG_LOG("TimerTask析构");
        // 在析构函数中执行定时任务
        if(_cancel == false) _task();
        // 在定时轮中释放对该定时任务的管理
        _release_task();
    }
};



/* 定时轮 */
class EventLoop;

static const int default_timerwheel_capacity = 60;

class TimerWheel
{
    using TaskPtr = std::shared_ptr<TimerTask>;
    using TaskWPtr = std::weak_ptr<TimerTask>;
private:
    size_t _capacity;// 定时轮大小
    std::vector<std::vector<TaskPtr>> _wheel;// 时间轮
    uint64_t _pointer;// 时间轮指针
    // uint64_t _task_id;// 定时任务id，自增长
    std::unordered_map<uint64_t, TaskWPtr> _timer_tasks;// 管理定时任务。\
                                需要根据定时任务id得到定时任务对象（不增加计数，所以这里使用weak_ptr）

    int _timerfd;// 定时器对象
    EventLoop* _eventloop;// 绑定的Loop
    Channel _timer_channel;// 对定时器上面的事件监控进行管理
private:
    /* 初始化定时器——创建+启动 */
    static int CreateTimerfd()
    {
        // 1. 创建定时器对象（阻塞式）
        int tfd = timerfd_create(CLOCK_MONOTONIC, 0);// CLOCK_MONOTONIC：以系统启动时间为计时基准值
        if(tfd < 0)
        {
            ERR_LOG("timerfd create failed");
            abort();
        }
        
        // DBG_LOG("timerfd: %d create success", tfd);

        // 2. 启动定时器
        struct itimerspec itime;
        // 第一次超时时间
        itime.it_value.tv_sec = 1;
        itime.it_value.tv_nsec = 0;
        // 第一次超时之后的超时时间
        itime.it_interval.tv_sec = 1;
        itime.it_interval.tv_nsec = 0;
        
        if(timerfd_settime(tfd, 0/* 使用相对时间 */, &itime, nullptr) < 0)
        {
            ERR_LOG("timerfd start failed");
            abort();
        }

        return tfd;
    }

    /* 重置定时器时间 */
    uint64_t ReadTimerfd()
    {
        uint64_t overtimeTime = 0;// 从上一次read后的超时次数
        ssize_t n = read(_timerfd, &overtimeTime, 8);
        if(n != sizeof(overtimeTime))
        {
            ERR_LOG("timerfd read failed");
            abort();
        }

        /* 可能存在这样一种情况，因为其他连接上的事件的业务处理的时间比较长，等到处理定时器描述符事件时，
        已经超时多次了，所以要返回这个超时次数，以便于后面指针走动时，超时几次，就走动几次 */
        return overtimeTime;
    }

    /* 定时轮运行，指针走动一个单位，执行任务 */
    void RunTasks()
    {
        _pointer = (_pointer + 1) % _capacity;
        _wheel[_pointer].clear();// 释放定时轮该位置的所有定时任务，执行它们的析构函数，执行任务回调
    }

    /* 时间到，超时，执行定时任务 */
    void OnTime()
    {
        /* 根据实际的超时次数，超时几次，定时轮的指针就走动几次，执行相应的回调函数 */
        uint64_t overtimeTime = ReadTimerfd();
        while(overtimeTime--) RunTasks();
    }

    /* 从管理中移除对指定定时任务的管理 */
    void ReleaseTimerTask(uint64_t task_id)
    {
        _timer_tasks.erase(task_id);
    }

    /* 添加定时任务 */
    void AddTimerTaskInLoopThread(uint64_t task_id, const TaskFunctor& task, uint32_t timeout)
    {
        // 1. 构建定时任务
        TaskPtr tp(new TimerTask(task_id, timeout, task, std::bind(&TimerWheel::ReleaseTimerTask, this, task_id)));
        // 2. 将定时任务添加到定时轮中的指定位置
        size_t pos = (_pointer + timeout) % _capacity;
        _wheel[pos].push_back(tp);
        // 3. 管理定时任务
        _timer_tasks.insert(std::make_pair(task_id, TaskWPtr(tp)));
        // 4. 定时任务id更新，这个在外部进行
        // ++_task_id;
    }
    /* 刷新定时任务——重置定时任务的超时时间 */
    void RefreshTimerTaskInLoopThread(uint64_t task_id)
    {
        // 1. 保证定时任务存在
        auto it = _timer_tasks.find(task_id);
        if(it == _timer_tasks.end()) return;

        // 2. 构建一个新的定时任务
        TaskPtr tp(it->second.lock());
        // 3. 添加该定时任务到定时轮
        size_t pos = (tp->TimeOut() + _pointer) % _capacity;
        _wheel[pos].push_back(tp);
    }
    /* 取消定时任务 */
    void CancelTimerTaskInLoopThread(uint64_t task_id)
    {
        // 1. 保证定时任务存在
        auto it = _timer_tasks.find(task_id);
        if(it == _timer_tasks.end()) return;

        // 2. 取消定时任务的回调执行
        // it->second.lock()->Cancel();
        TaskPtr tp(it->second.lock());
        if(tp) tp->Cancel();
    }
public:
    TimerWheel(EventLoop* eventloop): _capacity(default_timerwheel_capacity), _wheel(_capacity), _pointer(0), 
                                    _timerfd(CreateTimerfd()), _eventloop(eventloop), _timer_channel(_timerfd, _eventloop)
    {
        // 设置定时器的读时间回调，即超时回调
        _timer_channel.SetReadCallback(std::bind(&TimerWheel::OnTime, this));
        // 启动对定时器读事件的监控
        _timer_channel.EnableRead();
    }
    /* 是否存在某个定时任务 */
    bool HasTimerTask(uint64_t task_id) const
    {
        return _timer_tasks.count(task_id) > 0;
    }
    /* 为了保证线程安全，下面的操作都在创建定时任务的那个线程中执行 */
    void AddTimerTask(uint64_t task_id, const TaskFunctor& task, uint32_t timeout);
    void RefreshTimerTask(uint64_t task_id);
    void CancelTimerTask(uint64_t task_id);
};

#endif
